package pers.vic.generator.renren.service;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

import javax.annotation.Resource;

import org.apache.commons.configuration.Configuration;
import org.apache.commons.configuration.ConfigurationException;
import org.apache.commons.configuration.PropertiesConfiguration;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.Velocity;
import org.springframework.stereotype.Service;

import pers.vic.base.pager.Lookup;
import pers.vic.base.pager.PageInfo;
import pers.vic.base.service.BaseService;
import pers.vic.generator.configuration.ConfigurationOfGenerator;
import pers.vic.generator.renren.mapper.GeneratorMapper;
import pers.vic.generator.renren.model.ColumnEntity;
import pers.vic.generator.renren.model.ColumnExtend;
import pers.vic.generator.renren.model.TableEntity;

@Service
public class GeneratorService extends BaseService {

	@Resource
	private ConfigurationOfGenerator configurationOfGenerator;

	@Resource
	private GeneratorMapper generatorMapper;

	public PageInfo<TableEntity> list(Lookup lookup) {
		startPage(lookup);
		List<TableEntity> datas = generatorMapper.queryList(lookup);
		return PageInfo.instance(datas, lookup);
	}

	private List<ColumnEntity> queryColumns(String tableName) {
		return generatorMapper.queryColumns(tableName);
	}

	private TableEntity queryTable(String tableName) {
		return generatorMapper.queryTable(tableName);
	}

	/**
	 * 根据表名生成代码
	 * 
	 * @param moduleName
	 * @param packageName
	 */
	public byte[] generatorCode(String[] tableNames, String packageName, String moduleName) {
		ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
		ZipOutputStream zip = new ZipOutputStream(outputStream);

		for (String tableName : tableNames) {
			// 查询表信息
			TableEntity table = queryTable(tableName);
			// 查询列信息
			List<ColumnEntity> columns = queryColumns(tableName);
			// 生成代码
			generatorCode(table, packageName, moduleName, columns, zip);
		}
		IOUtils.closeQuietly(zip);
		return outputStream.toByteArray();
	}

	private void generatorCode(TableEntity table, String packageName, String moduleName, List<ColumnEntity> columns,
			ZipOutputStream zip) {
		
		Map<String, Object> map = getTemplateData(table, packageName, moduleName, columns);
		VelocityContext context = new VelocityContext(map);

		// 获取模板列表
		List<String> templates = getTemplates();
		for (String template : templates) {
			// 渲染模板
			StringWriter sw = new StringWriter();
			Template tpl = Velocity.getTemplate(template, "UTF-8");
			tpl.merge(context, sw);
			try {
				// 添加到zip
				String fileName = getFileName(template, table.getClassName(), configurationOfGenerator.getPackageName(),
						configurationOfGenerator.getModuleName());
				zip.putNextEntry(new ZipEntry(fileName));
				IOUtils.write(sw.toString(), zip, "UTF-8");
				IOUtils.closeQuietly(sw);
				zip.closeEntry();
			} catch (Exception e) {
				throw new RuntimeException("渲染模板失败，表名：" + table.getTableName(), e);
			}

		}
	}

	//获得渲染模板的数据
	private Map<String, Object> getTemplateData(TableEntity table, String packageName,  String moduleName, List<ColumnEntity> columns) {
		// 配置信息
		Configuration config = getConfig();
		boolean hasBigDecimal = false;
		String className = tableToJava(table.getTableName(), configurationOfGenerator.getTablePrefix());

		table.setClassName(className);
		// 第一个字母小写
		table.setClassname(StringUtils.uncapitalize(className));

		// 列信息
		for (ColumnEntity column : columns) {
			// 列名转换成Java属性名
			String attrName = columnToJava(column.getColumnName());
			column.setAttrName(attrName);
			column.setAttrname(StringUtils.uncapitalize(attrName));

			// 列的数据类型，转换成Java类型
			String attrType = config.getString(column.getDataType(), "unknowType");
			column.setAttrType(attrType);
			if (!hasBigDecimal && attrType.equals("BigDecimal")) {
				hasBigDecimal = true;
			}
			if ("Date".equals(attrType)) {
				table.setHasDate(true);
			}

			// 是否主键
			if ("PRI".equalsIgnoreCase(column.getColumnKey()) && table.getPk() == null) {
				table.setPk(column);
			}
			// 根据列是否是图片和日期 判断表中是否含有图片和日期
			ColumnExtend extend = ColumnExtend.initExtends(column);
			table.findExtend(extend);

		}
		// 没主键，则第一个字段为主键
		table.setColumns(columns);
		if (table.getPk() == null) {
			table.setPk(columns.get(0));
		}

		// 设置velocity资源加载器
		Properties prop = new Properties();
		prop.put("file.resource.loader.class", "org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader");
		Velocity.init(prop);

		// 包主路径
		String mainPath = configurationOfGenerator.getMainPath();
		mainPath = StringUtils.isBlank(mainPath) ? "com.vic" : mainPath;
		// 封装模板数据
		Map<String, Object> map = new HashMap<>();
		map.put("table", table);
		map.put("tableName", table.getTableName());
		map.put("comments", table.getComments());
		map.put("pk", table.getPk());
		map.put("className", table.getClassName());
		map.put("classname", table.getClassname());
		map.put("pathName", table.getClassname().toLowerCase());
		map.put("columns", table.getColumns());
		map.put("hasBigDecimal", hasBigDecimal);
		map.put("mainPath", mainPath);
		if (StringUtils.isEmpty(packageName)) {
			map.put("package", config.getString("packageName"));
		} else {
			map.put("package", packageName);
		}
		if (StringUtils.isEmpty(packageName)) {
			map.put("moduleName", config.getString("moduleName"));
		} else {
			map.put("moduleName", moduleName);
		}
		map.put("author", config.getString("author"));
		map.put("email", config.getString("email"));
		map.put("datetime", DateFormatUtils.format(new Date(), "yyyy-mm-dd HH:mm"));
		return map;
	}

	/**
	 * 获取文件名
	 */
	public static String getFileName(String template, String className, String packageName, String moduleName) {
		String packagePath = "generator" + File.separator + className;
		/*
		 * packagePath = "main" + File.separator + "java" + File.separator; if
		 * (StringUtils.isNotBlank(packageName)) { packagePath +=
		 * packageName.replace(".", File.separator) + File.separator + moduleName +
		 * File.separator; }
		 */

		// 实体
		if (template.contains("Entity.java.vm")) {
			return packagePath + File.separator + className + ".java";
		}
		// 查询条件
		if (template.contains("Lookup.java.vm")) {
			return packagePath + File.separator + className + "Lookup.java";
		}

		// mapper.java
		if (template.contains("Dao.java.vm")) {
			return packagePath + File.separator + className + "Mapper.java";
		}

		// service
		if (template.contains("Service.java.vm")) {
			return packagePath + File.separator + className + "Service.java";
		}

		// controller
		if (template.contains("Controller.java.vm")) {
			return packagePath + File.separator + className + "Controller.java";
		}
		// mapper.xnl
		if (template.contains("Dao.xml.vm")) {
			return packagePath + File.separator + className + "Mapper.xml";
		}

		// list.jsp
		if (template.contains("list.jsp.vm")) {
			return packagePath + File.separator + "list.jsp";
		}
		// form.jsp
		if (template.contains("form.jsp.vm")) {
			return packagePath + File.separator + "form.jsp";
		}

		return getWinskyFileName(template, className, packageName, moduleName);
//        return null;
	}

	public static String getWinskyFileName(String template, String className, String packageName, String moduleName) {
		String packagePath = "generator" + File.separator + "winsky";
		// 实体
		if (template.contains("winsky_entity.java.vm")) {
			return packagePath + File.separator + className + ".java";
		}

		// mapper.java
		if (template.contains("winsky_dao.java.vm")) {
			return packagePath + File.separator + className + "Mapper.java";
		}

		// service
		if (template.contains("winsky_service.java.vm")) {
			return packagePath + File.separator + className + "Service.java";
		}

		// controller
		if (template.contains("winsky_controller.java.vm")) {
			return packagePath + File.separator + className + "Controller.java";
		}
		// mapper.xnl
		if (template.contains("winsky_dao.xml.vm")) {
			return packagePath + File.separator + className + "Mapper.xml";
		}
		return null;
	}

	public static List<String> getTemplates() {
	/*	Map<String, String> templateMap = new HashMap<>();
		templateMap.put("entity", "template/Entity.java.vm");
		templateMap.put("lookup", "template/Lookup.java.vm");
		templateMap.put("dao.java", "template/Dao.java.vm");
		templateMap.put("dao.xml", "template/Dao.xml.vm");
		templateMap.put("service", "template/Service.java.vm");
		templateMap.put("controller", "template/Controller.java.vm");
		templateMap.put("list", "template/list.jsp.vm");
		templateMap.put("form", "template/form.jsp.vm");*/
		
		List<String> templates = new LinkedList<>();
		templates.add("template/Entity.java.vm");
		templates.add("template/Dao.xml.vm");
		templates.add("template/Dao.java.vm");
		templates.add("template/Service.java.vm");
		templates.add("template/Lookup.java.vm");
		templates.add("template/Controller.java.vm");
		templates.add("template/list.jsp.vm");
		templates.add("template/form.jsp.vm");

//		addWinskyTemplate(templates);
 
 
		return templates;
	}

	/*** 新增WINSKY的模板 ***/
	private static void addWinskyTemplate(List<String> templates) {
		templates.add("template/winsky/winsky_entity.java.vm");
		templates.add("template/winsky/winsky_dao.java.vm");
		templates.add("template/winsky/winsky_dao.xml.vm");
		templates.add("template/winsky/winsky_service.java.vm");
		templates.add("template/winsky/winsky_controller.java.vm");
	}

	/**
	 * 表名转换成Java类名
	 */
	private String tableToJava(String tableName, String tablePrefix) {
		if (StringUtils.isNotBlank(tablePrefix)) {
			tableName = tableName.replaceFirst(tablePrefix, "");
		}
		return columnToJava(tableName);
	}

	/**
	 * 列名转换成Java属性名
	 */
	public static String columnToJava(String columnName) {
//		return WordUtils.capitalizeFully(columnName, new char[] { '_' }).replace("_", "");//aaBBcc-->aabbcc  不是我想要的
		return underlineToCamel(columnName);
	}

	/**
	 * 下划线转驼峰 但是不小写
	 * 
	 * @param str
	 * @return
	 */
	static String UNDERLINE = "_";

	public static String underlineToCamel(String str) {
		if (StringUtils.isEmpty(str)) {
			return str;
		}
		StringBuffer sb = new StringBuffer();
		for (String s : str.split(UNDERLINE)) {
			sb.append(StringUtils.capitalize(s));
		}
		return sb.toString();
	}

	/**
	 * 获取配置信息
	 */
	public static Configuration getConfig() {
		try {
			return new PropertiesConfiguration("generator-config.properties");
		} catch (ConfigurationException e) {
			throw new RuntimeException("获取配置文件失败，", e);
		}
	}

	public Map<String, String> findTableDetail(String tableName, String packageName, String moduleName) {
		// 查询表信息
		TableEntity table = queryTable(tableName);
		// 查询列信息
		List<ColumnEntity> columns = queryColumns(tableName);
		
		Map<String, Object> map = getTemplateData(table, packageName, moduleName, columns);
		VelocityContext context = new VelocityContext(map);

		// 获取模板列表
		List<String> templates = getTemplates();
		Map<String, String> data = new LinkedHashMap<>();
		for (String template : templates) {
			// 渲染模板
			StringWriter sw = new StringWriter();
			Template tpl = Velocity.getTemplate(template, "UTF-8");
			tpl.merge(context, sw);
			try {
				// 添加到zip
				String fileName = getFileName(template, table.getClassName(), configurationOfGenerator.getPackageName(),
						configurationOfGenerator.getModuleName());
				data.put(fileName.replace("generator" + File.separator, "").replace(File.separator, "_"), sw.toString());
			} catch (Exception e) {
				throw new RuntimeException("渲染模板失败，表名：" + table.getTableName(), e);
			}

		}
		return data;
	}

}
